SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
2.6 KB · 52 lines typescript
Raw Blame History
1import { NextResponse } from 'next/server';2import { getCurrentUser } from '@/lib/auth/session';3import { getCollectionDetail } from '@/lib/account/queries';4import { toCsv } from '@/lib/account/portfolio';56export const dynamic = 'force-dynamic';78const COLUMNS = ['item_id', 'asset_id', 'asset_slug', 'title', 'category', 'variant', 'grader', 'grade', 'cert', 'serial', 'quantity', 'acquired_at', 'purchase_price', 'currency', 'purchase_price_usd', 'source', 'condition', 'value_usd', 'value_source', 'confidence', 'gain_usd', 'gain_pct', 'tags', 'notes'];910export async function GET(req: Request, ctx: { params: Promise<{ id: string }> }) {11  const u = await getCurrentUser();12  if (!u) return NextResponse.json({ error: 'unauthorized' }, { status: 401 });13  const { id } = await ctx.params;14  const detail = await getCollectionDetail(u.id, id);15  if (!detail) return NextResponse.json({ error: 'not found' }, { status: 404 });16  const format = new URL(req.url).searchParams.get('format') ?? 'csv';17  const rows = detail.summary.items.map((i) => {18    const src = detail.items.find((x) => x.id === i.id)!;19    return {20      item_id: i.id,21      asset_id: i.assetId,22      asset_slug: src.assetSlug,23      title: i.title,24      category: i.categorySlug,25      variant: src.variantLabel,26      grader: i.grader,27      grade: i.grade,28      cert: src.certificationNumber,29      serial: src.serial,30      quantity: i.quantity,31      acquired_at: i.acquiredAt,32      purchase_price: src.purchasePriceNative,33      currency: src.acquiredCurrency,34      purchase_price_usd: i.purchasePriceUsd,35      source: src.source,36      condition: src.condition,37      value_usd: i.valueUsd,38      value_source: i.valueSource,39      confidence: i.confidence,40      gain_usd: i.gainUsd,41      gain_pct: i.gainPct,42      tags: src.tags.join('|'),43      notes: src.notes,44    };45  });46  const stamp = new Date().toISOString().slice(0, 10);47  if (format === 'json') {48    return new NextResponse(JSON.stringify({ collection: detail.collection.name, exportedAt: new Date().toISOString(), summary: { valueUsd: detail.summary.valueUsd, costBasisUsd: detail.summary.costBasisUsd, gainUsd: detail.summary.gainUsd, returnPct: detail.summary.returnPct, items: detail.summary.itemCount }, items: rows }, null, 2), { headers: { 'content-type': 'application/json', 'content-disposition': `attachment; filename="rareindex-${detail.collection.publicSlug ?? id}-${stamp}.json"` } });49  }50  return new NextResponse(toCsv(rows, COLUMNS), { headers: { 'content-type': 'text/csv; charset=utf-8', 'content-disposition': `attachment; filename="rareindex-${detail.collection.publicSlug ?? id}-${stamp}.csv"` } });51}52